package com.example.demo.controller;

import cn.idev.excel.FastExcel;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.common.Func;
import com.example.demo.common.Result;
import com.example.demo.common.constant.RedisKey;
import com.example.demo.common.constant.RuleCode;
import com.example.demo.entity.User;
import com.example.demo.entity.UserVO;
import com.example.demo.entity.excel.UserExcel;
import com.example.demo.exception.BusinessException;
import com.example.demo.listener.DemoDataListener;
import com.example.demo.verify.ExcelValid;
import com.example.demo.verify.FileValid;
import com.example.demo.verify.ParamValid;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import jxl.format.Alignment;
import jxl.format.UnderlineStyle;
import jxl.format.VerticalAlignment;
import jxl.write.*;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/sign")
@Api(tags = "2、生成文件管理")
@ApiSupport(author = "罗海林", order = 2)
@Validated
@RequiredArgsConstructor
public class ExportController {

    private final RedisTemplate<String, String> redis;

    @PostConstruct
    public void init() {
        Func.print("初始化完成:ExportController");
    }

    @PostMapping("/createWord")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "files", value = "上传文件"),
            @ApiImplicitParam(name = "isClear", value = "清除标识")
    })
    @ApiOperation(value = "导入-生成Word（手工操作）", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 1)
    public Result createWord(
            @ExcelValid(
                    name = { "姓名", "身份证号", "公司名称", "专票金额", "普票金额", "未开票金额", "联络员" },
                    rule = { "wick", "wick,range(15-18)", "wick", "money", "money", "money", "wick" },
                    empty = { false, false, false, true, true, true, false }
            ) @RequestParam MultipartFile files,
            @ParamValid(name = "清除标识", rule = RuleCode.noEmpty) @RequestParam String isClear
    ) {
        String[] fieldList = {"name", "idcard", "company", "simoney", "cimoney", "nmimoney", "belonger"};
        List<JSONObject> excelData = Func.getExcelData(files, fieldList, 1);

        String templatePath = Func.getRootPath() + "/file/template/";
        String filePath = Func.getRootPath() + "/file/download/word/";
        String templateName = "apply1.ftl";
        String[][] fileNameRole = {{"name", "-"}, {"idcard", ""}};
        Func.createWordWithTemplate(excelData, fileNameRole, templatePath, templateName, filePath, true);

        Result res = new Result();
        res.ok();
        return res;
    }

    @PostMapping("/importFileTest")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "files", value = "上传文件")
    })
    @ApiOperation(value = "上传文件格式校验", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 2)
    public Result createWord(@FileValid(suffix = { "xls", "doc", "docx" }, size = "2M") @RequestParam MultipartFile files) {
        Result res = new Result();
        res.ok();
        return res;
    }

    @PostMapping("/exportExcel")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileName", value = "导出文件名")
    })
    @ApiOperation(value = "导出Excel表格", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 3)
    public Result exportExcel(@RequestParam String fileName) throws WriteException, IOException {
        // 设置表头格式
        WritableCellFormat format1 = new WritableCellFormat(new WritableFont(WritableFont.createFont("宋体"), 11, WritableFont.BOLD, false, UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.BLACK));
        format1.setAlignment(Alignment.CENTRE);
        format1.setVerticalAlignment(VerticalAlignment.CENTRE);
        format1.setWrap(true);

        // 设置表格内容格式
        WritableCellFormat format2 = new WritableCellFormat(new WritableFont(WritableFont.createFont("宋体"), 11, WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.BLACK));
        format2.setAlignment(Alignment.CENTRE);
        format2.setVerticalAlignment(VerticalAlignment.CENTRE);
        format2.setWrap(true);

        // 设置表格路径
        String filePath = Func.getRootPath() + "/file/download/excel/" + fileName + ".xls";
        File file = new File(filePath);
        WritableWorkbook workbook = jxl.Workbook.createWorkbook(file);
        WritableSheet sheet = workbook.createSheet("报税信息", 0);

        // 设置表头与列宽
        Object[][] header = {{"姓名", 15}, {"身份证号", 25}, {"个体户名称", 30}, {"专票金额", 15}, {"普票金额", 15}, {"未开票金额", 15}, {"执行结果", 40}, {"归属者", 15}};
        int cols = 8;
        for (int i = 0; i < cols; i++) {
            sheet.addCell(new Label(i, 0, header[i][0].toString(), format1));
            sheet.setColumnView(i, Integer.parseInt(header[i][1].toString()));
        }

        // 设置表格内容
        String[][] content = {{"罗海林", "450502198603230316", "大海集团软件工作室", "80000", "3500", "0", "报税完毕", "林春丽"}};
        int rows = content.length;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                sheet.addCell(new Label(j, i, content[i][j], format2));
            }
        }

        workbook.write();
        workbook.close();

        // 设置文件过期时间，过期时触发删除操作
        /*String key = String.format("expire:download:excel:%s", fileName + ".xls");
        redis.opsForValue().set(key, "1", 30, TimeUnit.MINUTES);*/
        String key1 = String.format(RedisKey.EXPIRE + "%s", filePath);
        redis.opsForValue().set(key1, "1", 5, TimeUnit.SECONDS);


        Result res = new Result();
        res.ok();
        res.put("path", filePath);
        return res;
    }

    @PostMapping("/exportExcelByJxls")
    @ApiOperation(value = "导出Excel表格（Jxls）", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 4)
    public Result exportExcelByJxls() throws IOException {
        // 这种方法无法识别模板中的合并单元格
        InputStream inputStream = new FileInputStream("file/template/template.xlsx");
        OutputStream outputStream = new FileOutputStream("file/template/report.xlsx");

        JSONObject js1 = new JSONObject();
        js1.put("name", "周罗丹");
        js1.put("area", "福建");

        JSONObject js2 = new JSONObject();
        js2.put("name", "林春丽");
        js2.put("area", "广东");

        JSONObject js3 = new JSONObject();
        js3.put("name", "陈艺文");
        js3.put("area", "广西");

        JSONArray list = new JSONArray();
        list.add(js1);
        list.add(js2);
        list.add(js3);

        Context context = new Context();
        context.putVar("employees", list);

        JxlsHelper.getInstance().processTemplate(inputStream, outputStream, context);

        inputStream.close();
        outputStream.close();

        Result res = new Result().ok();
        return res;
    }

    @PostMapping("/importExcel")
    @ApiOperation(value = "导入Excel表格（fastExcel）", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 5)
    public Result importExcel() {
        String fileName = Func.getRootPath() + "/file/upload/women.xlsx";

        /*
         * 1、传入的是MultipartFile，用 file.getInputStream() 替换 pathName
         * 2、传入的是Base64，用 new ByteArrayInputStream(bytes) 转成 InputStream
         **/
        FastExcel.read(fileName, UserExcel.class, new DemoDataListener()).sheet().doRead();

        Result res = new Result().ok();
        return res;
    }

    @PostMapping("/exportExcelByEasy")
    @ApiOperation(value = "导出Excel表格（Easy）", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 6)
    public Result exportExcelByEasy() {
        List<UserExcel> userList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            UserExcel user = new UserExcel();
            user.setName("全晴" + i);
            user.setOtherName("楚乔" + i);
            user.setAge(i + 1);
            userList.add(user);
        }

        // 这种方式只能生成默认样式的表格，若需要特定样式，请设置模板导出
        String fileName = Func.getRootPath() + "/file/download/excel/demo.xlsx";
        FastExcel.write(fileName, UserExcel.class).sheet("同事信息").doWrite(userList);

        Result res = new Result().ok();
        return res;
    }

    @PostMapping("/convertToUserDTO")
    @ApiOperation(value = "实体类与DTO类拷贝", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 7)
    public Result convertToUserDTO() {
        User user = new User();
        user.setId(10);
        user.setUserId("0507100229");
        user.setName("罗海林");
        user.setUsername("lhlysyl543Dahai");

        UserVO info = new UserVO();

        /**
         * 类型相同但名称不同、类型不同但名称相同，都无法拷贝
         * 相互拷贝的类，无论是：
         * 1、较多属性的类——>较少属性的类
         * 2、较少属性的类——>较多属性的类
         * 结论：都只拷贝类型与名称相同的属性
         **/
        BeanCopier.create(User.class, UserVO.class, false).copy(user, info, null);

        Result res = new Result().ok();
        res.put("userVO", info);
        return res;
    }

    @PostMapping("/convertExcelToPdf")
    @ApiOperation(value = "Excel转换Pdf", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 8)
    public Result convertExcelToPdf() {
        try {
            // 读取 Excel 文件
            FileInputStream excelFile = new FileInputStream("file/template/report.xlsx");
            Workbook workbook = new XSSFWorkbook(excelFile);
            Sheet sheet = workbook.getSheetAt(0);

            // 创建 PDF 文档
            Document document = new Document();
            PdfWriter.getInstance(document, new FileOutputStream("file/template/report.pdf"));
            document.open();

            // 设置中文字体（字体文件路径在Linux上要重新配置）
            BaseFont font1 = BaseFont.createFont("C:/WINDOWS/Fonts/SIMSUN.TTC,1", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            Font font = new Font(font1, 12, Font.NORMAL);

            // 创建 PDF 表格，列数与 Excel 表格的列数相同
            int columnCount = sheet.getRow(0).getLastCellNum();
            PdfPTable pdfTable = new PdfPTable(columnCount);

            // 遍历 Excel 表格的每一行（只能应对简单表格，有合并单元格的表格不适合）
            for (Row row : sheet) {
                // 遍历当前行的每一个单元格
                for (Cell cell : row) {
                    // 获取单元格的值
                    String cellValue = getCellValueAsString(cell);
                    // 创建 PDF 表格的单元格
                    PdfPCell pdfCell = new PdfPCell(new Phrase(cellValue, font));
                    // 将单元格添加到 PDF 表格中
                    pdfTable.addCell(pdfCell);
                }
            }

            // 将 PDF 表格添加到 PDF 文档中
            document.add(pdfTable);

            // 关闭文档和 Excel 文件
            document.close();
            workbook.close();
            excelFile.close();
        }
        catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("Excel 转 PDF 失败：" + e.getMessage());
        }

        Result res = new Result().ok();
        return res;
    }

    // 将单元格的值转换为字符串
    private String getCellValueAsString(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case Cell.CELL_TYPE_STRING:
                return cell.getStringCellValue();
            case Cell.CELL_TYPE_NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                } else {
                    return String.valueOf(cell.getNumericCellValue());
                }
            case Cell.CELL_TYPE_BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case Cell.CELL_TYPE_FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }

    @PostMapping("/createHtml")
    @ApiOperation(value = "按模板生成html", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 9)
    public Result createHtml() {
        FileWriter fileWriter = null;

        try {
            Configuration config = new Configuration(Configuration.VERSION_2_3_30);
            String rootPath = Func.getRootPath();
            String path = rootPath + "/file/template";
            TemplateLoader templateLoader = new FileTemplateLoader(new File(path));
            config.setTemplateLoader(templateLoader);
            Template template = config.getTemplate("home.ftl", Locale.SIMPLIFIED_CHINESE);

            JSONObject data = new JSONObject();
            data.put("title", "同学情况介绍");
            data.put("name", "王彦远");
            data.put("age", "39");
            data.put("work", "国企人事HR");
            data.put("school", "北海市第三中学");

            String id = Func.getSnowID();
            fileWriter = new FileWriter(rootPath + "/file/download/html/" + id + ".html");
            template.process(data, fileWriter);
        }
        catch (Exception ex) {
            throw new BusinessException("生成HTML文件异常:" + ex);
        }
        finally {
            if (fileWriter != null) {
                try {
                    fileWriter.close();
                }
                catch (Exception ignored) {}
            }
        }

        Result res = new Result().ok();
        return res;
    }






    /* 获取DTO中各字段上面的注解 */
    /*public List<Field> getAllFields(Object object) {
        List<Field> fields = new ArrayList<>();

        Class<?> clazz = object.getClass();
        while (clazz != null) {
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                fields.add(field);
            }
            clazz = clazz.getSuperclass();
        }

        return fields;
    }*/

}
